package lmproto

import "fmt"

// PacketType 包类型
type PacketType uint8

// Framer 包的基础framer
type Framer struct {
	PacketType      PacketType
	RemainingLength uint32 // 控制报文总长度等于固定报头的长度加上剩余长度
	NoPersist       bool   // 是否不持久化
	RedDot          bool   // 是否显示红点
	SyncOnce        bool   // 此消息只被同步或被消费一次
	DUP             bool   // 是否是重发消息
}

// ToFixHeaderUint8 ToFixHeaderUint8
func ToFixHeaderUint8(f Frame) uint8 {
	typeAndFlags := encodeBool(f.GetDUP())<<3 | encodeBool(f.GetsyncOnce())<<2 | encodeBool(f.GetRedDot())<<1 | encodeBool(f.GetNoPersist())
	return byte(int(f.GetPacketType()<<4) | typeAndFlags)
}

// FramerFromUint8 FramerFromUint8
func FramerFromUint8(v uint8) Framer {
	p := Framer{}
	p.NoPersist = (v & 0x01) > 0
	p.RedDot = (v >> 1 & 0x01) > 0
	p.SyncOnce = (v >> 2 & 0x01) > 0
	p.DUP = (v >> 3 & 0x01) > 0
	p.PacketType = PacketType(v >> 4)
	return p
}

// GetPacketType GetPacketType
func (f Framer) GetPacketType() PacketType {
	return f.PacketType
}

// GetRemainingLength 包剩余长度
func (f Framer) GetRemainingLength() uint32 {
	return f.RemainingLength
}

// GetNoPersist 是否不持久化
func (f Framer) GetNoPersist() bool {
	return f.NoPersist
}

// GetRedDot 是否显示红点
func (f Framer) GetRedDot() bool {
	return f.RedDot
}

// GetsyncOnce 是否只被同步一次
func (f Framer) GetsyncOnce() bool {
	return f.SyncOnce
}

// GetDUP 是否是重发消息
func (f Framer) GetDUP() bool {
	return f.DUP
}

func (f Framer) String() string {
	return fmt.Sprintf("packetType: %s remainingLength:%d NoPersist:%v redDot:%v syncOnce:%v DUP:%v", f.GetPacketType().String(), f.RemainingLength, f.NoPersist, f.RedDot, f.SyncOnce, f.DUP)
}

type Setting struct {
	Receipt bool // 消息已读回执，此标记表示，此消息需要已读回执
}

func (s Setting) ToUint8() uint8 {
	return uint8(encodeBool(s.Receipt) << 7)
}

func SettingFromUint8(v uint8) Setting {
	s := Setting{}
	s.Receipt = (v >> 7 & 0x01) > 0
	return s
}

// 包类型
const (
	Reserved   PacketType = iota // 保留位
	CONNECT                      // 客户端请求连接到服务器(c2s)
	CONNACK                      // 服务端收到连接请求后确认的报文(s2c)
	SEND                         // 发送消息(c2s)
	SENDACK                      // 收到消息确认的报文(s2c)
	RECV                         // 收取消息(s2c)
	RECVACK                      // 收取消息确认(c2s)
	PING                         //ping请求
	PONG                         // 对ping请求的相应
	DISCONNECT                   // 请求断开连接
)

func (p PacketType) String() string {
	switch p {
	case CONNECT:
		return "CONNECT"
	case CONNACK:
		return "CONNACK"
	case SEND:
		return "SEND"
	case SENDACK:
		return "SENDACK"
	case RECV:
		return "RECV"
	case RECVACK:
		return "RECVACK"
	case PING:
		return "PING"
	case PONG:
		return "PONG"
	case DISCONNECT:
		return "DISCONNECT"
	}
	return "UNKNOWN"
}

// ReasonCode 原因码
type ReasonCode uint8

const (
	// ReasonError 错误
	ReasonError ReasonCode = iota
	// ReasonSuccess 成功
	ReasonSuccess
	// ReasonAuthFail 认证失败
	ReasonAuthFail
	// ReasonSubscriberNotExist 订阅者在频道内不存在
	ReasonSubscriberNotExist
	// ReasonInBlacklist 在黑名单列表里
	ReasonInBlacklist
	// ReasonChannelNotExist 频道不存在
	ReasonChannelNotExist
	// ReasonUserNotOnNode 用户没在节点上
	ReasonUserNotOnNode
	// ReasonSenderOffline // 发送者离线了，这条消息将发不成功
	ReasonSenderOffline
	// ReasonMsgKeyError 消息key错误 说明消息不合法
	ReasonMsgKeyError
	// ReasonPayloadDecodeError payload解码失败
	ReasonPayloadDecodeError
	// ReasonForwardSendPacketError 转发发送包失败
	ReasonForwardSendPacketError
)

func (r ReasonCode) String() string {
	switch r {
	case ReasonError:
		return "ReasonError"
	case ReasonSuccess:
		return "ReasonSuccess"
	case ReasonAuthFail:
		return "ReasonAuthFail"
	case ReasonSubscriberNotExist:
		return "ReasonSubscriberNotExist"
	}
	return "UNKNOWN"
}

// Byte 转换为byte
func (r ReasonCode) Byte() byte {
	return byte(r)
}

// DeviceFlag 设备类型
type DeviceFlag uint8

const (
	// APP APP
	APP DeviceFlag = iota
	// WEB WEB
	WEB = 1
	// SYSTEM 系统
	SYSTEM = 2
)

// DeviceLevel 设备等级
type DeviceLevel uint8

const (
	// DeviceLevelSlave 从设备
	DeviceLevelSlave DeviceLevel = 0
	// DeviceLevelMaster 主设备
	DeviceLevelMaster DeviceLevel = 1
)

// ToUint8 转换为uint8
func (r DeviceFlag) ToUint8() uint8 {
	return uint8(r)
}

func (r DeviceFlag) String() string {
	switch r {
	case APP:
		return "APP"
	case WEB:
		return "WEB"
	case SYSTEM:
		return "SYSTEM"
	}
	return fmt.Sprintf("%d", r)
}

// Frame 接口
type Frame interface {
	GetPacketType() PacketType
	GetRemainingLength() uint32
	// GetPersist 是否存储
	GetNoPersist() bool
	// GetRedDot 是否显示红点
	GetRedDot() bool
	// GetsyncOnce 是否只被同步一次
	GetsyncOnce() bool
	// 是否是重发的消息
	GetDUP() bool
}
